package com.block.source.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.block.common.constant.RedisKeyConstant;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.SourceCountDTO;
import com.block.common.model.vo.ResultInfoVO;
import com.block.common.service.ISourceDataCountInfoService;
import com.block.common.service.LogFactory;
import com.block.common.service.merchant.IMerchantThirdBaseInfoService;
import com.block.db.entity.*;
import com.block.db.mapper.MerchantThirdProtocolInfoMapper;
import com.block.source.model.dto.SourceMatchResultDTO;
import com.block.source.service.*;
import com.block.test.reqbean.source.CheckUserInfo;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Service
public class SourceCheckUserService implements ISourceCheckUserService {

    private static final Logger log = LogFactory.getSourceLog();

    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    ISourceMemberDataInfoTempService sourceMemberDataInfoTempService;
    @Autowired
    ISourceMemberDataInfoHistoryService sourceMemberDataInfoHistoryService;
    @Autowired
    ISourceMatchHandleService sourceMatchHandleService;
    @Autowired
    ISourcePushResultInfoService sourcePushResultInfoService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    IMerchantThirdBaseInfoService merchantThirdBaseInfoService;
    @Resource
    MerchantThirdProtocolInfoMapper merchantThirdProtocolInfoMapper;
    @Autowired
    ISourceDataCountInfoService sourceDataCountInfoService;


    @Override
    public ResultInfoVO sourceCheckUser1(SourceBaseInfo sourceBaseInfo, CheckUserInfo userDataParam, ChannelInfo channelBaseInfo, ApplicationInfo applicationInfo){

        try {

            // 添加撞库锁
            String mobileLockKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMD5_LOCK_KEY, userDataParam.getMobileMd5());
            // 30分钟订单会超时，锁30分钟也会超时
            Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent(mobileLockKey, userDataParam.getMobileMd5(), 30, TimeUnit.MINUTES);
            if (!lockSuccess){
                return ResultInfoVO.error("该手机号正在进件，请稍后再试");
            }

            CountDownLatch countDownLatch = new CountDownLatch(1);
            Future<ResultInfoVO> merchantInfoFuture = ThreadUtil.execAsync(new Callable<ResultInfoVO>(){
                @Override
                public ResultInfoVO call() throws Exception {

                    ResultInfoVO resultInfoVO = sourceCheckUser(sourceBaseInfo, userDataParam, channelBaseInfo, applicationInfo);
                    countDownLatch.countDown();
                    return resultInfoVO;
                }
            });

            // 线索设置的撞库超时时间
            long  checkTimeout = sourceBaseInfo.getCheckTimeout() - 200;
            countDownLatch.await(checkTimeout, TimeUnit.MILLISECONDS);

            if (merchantInfoFuture.isDone()){
                ResultInfoVO resultInfoVO = merchantInfoFuture.get();

                if (resultInfoVO.isSuccess()){
                    JSONObject data = resultInfoVO.getData();
                    String status = data.getString("status");
                    if (StrUtil.equals(status, "200")){
                        SourceCountDTO sourceCountDTO = new SourceCountDTO(sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName());
                        sourceCountDTO.setCheckPassNumber(1);
                        sourceDataCountInfoService.saveSourceCountInfo(sourceCountDTO);
                    }
                }
                else{
                    // 撞库失败，删除掉 mobileMd5 撞库锁
                    stringRedisTemplate.delete(mobileLockKey);
                }
                return resultInfoVO;
            }

            // 撞库没有执行完成，删除掉 mobileMd5 撞库锁
            stringRedisTemplate.delete(mobileLockKey);

        }catch (Exception exception){
            exception.printStackTrace();
            String message = StrUtil.format("【channelCode】{}【channelName】{}【data】{}",
            sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), JSON.toJSONString(userDataParam));
            notifyExceptionService.notifyNoticeInfo("SOURCE_CHECK_EXC", "撞库操作异常", message, exception);

        }
        return ResultInfoVO.error("撞库失败");
    }

    @Override
    public ResultInfoVO sourceCheckUser(SourceBaseInfo sourceBaseInfo, CheckUserInfo checkUserInfo, ChannelInfo channelBaseInfo, ApplicationInfo applicationInfo) throws Exception {

        try {

            // 同一个渠道，相同的手机号md5 ，6个小时内只能撞库一次，，，撞库失败也只能撞一次
            String  mobileMd5 = checkUserInfo.getMobileMd5();
            String channelCode = sourceBaseInfo.getChannelCode();
            String redisKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMD5_CHANNEL, channelCode, mobileMd5);
            // 查询缓存是否有
            boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "0", 6, TimeUnit.HOURS);
            if (!setIfAbsent){
                // 6小时之内不能重复撞库
                return ResultInfoVO.error("同一手机号6小时内不能重复撞库");
            }

            JSONObject returnObject = new JSONObject();

            log.info("【线索撞库】【channelCode】{}【channelName】{}【UserDataParam】{}",
                    channelBaseInfo.getChannelCode(), channelBaseInfo.getChannelName(), JSON.toJSONString(checkUserInfo));

            // 临时数据入库
            ResultInfoVO resultInfoVO = sourceMemberDataInfoTempService.saveMemberData(checkUserInfo, channelBaseInfo, applicationInfo);
            if (!resultInfoVO.isSuccess()){
                return resultInfoVO;
            }

            // 订单号
            JSONObject data = resultInfoVO.getData();
            SourceMemberDataInfoTemp dataInfoTemp = (SourceMemberDataInfoTemp) data.get("dataInfo");

            // 异步保存
             sourceMemberDataInfoHistoryService.saveMemberData(dataInfoTemp);

            // 根据临时结果去进行撞库操作
            SourceMatchResultDTO resultDTO = sourceMatchHandleService.toMatchMerchant2(sourceBaseInfo, dataInfoTemp);
            // 线索撞库完成

            MerchantThirdBaseInfo thirdBaseInfo = null;
            log.info("【线索撞库】撞库完成【resultDTO】{}", JSON.toJSONString(resultDTO));
            if (resultDTO.isSuccess()){

                SourcePushResultInfo sourcePushResultInfo = sourcePushResultInfoService.savePushResultInfo(dataInfoTemp, resultDTO, sourceBaseInfo);

                returnObject.put("status", "000");
                returnObject.put("remark", "撞库通过，可以推送");
                returnObject.put("orderNo", sourcePushResultInfo.getId() + "");

                // 如果机构类型为apis，
                // 则查询机构商户的信息（需要返回 商户产品信息）
                if (StrUtil.equals(InfoTypeEnum.APIS.getValue(), sourcePushResultInfo.getInfoType())){

                    MemberPushOrderInfo pushOrderInfo = resultDTO.getPushOrderInfo();
                    // TODO selectEntityByOrderNo 用的还是pushID, UK, 命名不对劲; 240827
                    thirdBaseInfo = merchantThirdBaseInfoService.selectEntityByOrderNo(pushOrderInfo.getPushId());
                    List<MerchantThirdProtocolInfo> thirdProtocolInfoList = merchantThirdProtocolInfoMapper.selectListByThirdId(thirdBaseInfo.getId());

                    returnObject.put("thirdBaseInfo", thirdBaseInfo);
                    returnObject.put("thirdProtocolInfoList", thirdProtocolInfoList);
                }
            } else {
                returnObject.put("status", "999");
                returnObject.put("remark", "撞库失败");
            }

            return ResultInfoVO.success(returnObject);
        }catch (Exception exception){
            String message = StrUtil.format("【channelCode】{}【channelName】{}【data】{}",
                    sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), JSON.toJSONString(checkUserInfo));
            notifyExceptionService.notifyNoticeInfo("SOURCE_CHECK_EXC111", "撞库操作异常", message, exception);
            throw exception;
        }
    }

}
